package com.czg.jsonschema2lombokpojo.rules;

import static org.apache.commons.lang3.StringUtils.*;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

import com.czg.jsonschema2lombokpojo.Schema;
import com.czg.jsonschema2lombokpojo.config.GenerationConfig;
import com.czg.jsonschema2lombokpojo.exception.GenerationException;

import com.fasterxml.jackson.databind.JsonNode;
import com.sun.codemodel.JClassContainer;
import com.sun.codemodel.JPackage;
import com.sun.codemodel.JType;
import org.apache.commons.io.FilenameUtils;

/**
 * 应用JSONschema。
 *
 */
public class SchemaRule implements Rule<JClassContainer, JType> {

    private final RuleFactory ruleFactory;

    protected SchemaRule(RuleFactory ruleFactory) {
        this.ruleFactory = ruleFactory;
    }

    /**
     * 应用此schema规则执行所需的代码生成步骤。
     * <p>
     * 在一个schema文档的根应该应用这个规则（schema文档包含一个schema），但也在文档中的许多地方。
     * object类型的每个属性本身都是由一个schema定义的，数组的items属性是一个schema，schema的additionalProperties属性也是一个schema。
     * <p>
     * 如果schema值是$ref，则假定ref URI适用于URL（将从中读取内容）。之前遇到过ref URI的地方，由该schema创建的根Java类型将被重用（生成步骤不会重复）。
     *
     * @param schema 在其中应用该schema规则的schema
     */
    @Override
    public JType apply(String nodeName, JsonNode schemaNode, JsonNode parent, JClassContainer generatableType, Schema schema) {
        System.out.println(this.getClass().getName() + " apply field:" + nodeName);

        if (schemaNode.has("$ref")) {
            String path = schemaNode.get("$ref").asText();
            final String nameFromRef = nameFromRef(path);

            schema = ruleFactory.getSchemaStore().create(schema, path, ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters());
            schemaNode = schema.getContent();

            if (schema.isGenerated()) {
                return schema.getJavaType();
            }

            return apply(nameFromRef != null ? nameFromRef : nodeName, schemaNode, parent, generatableType, schema);
        }

        JType javaType;
        if (schemaNode.has("enum")) {
            ruleFactory.getGenerationConfig().isEnumInSubPackage();
            JPackage enumPackage = ruleFactory.getGenerationConfig().isEnumInSubPackage()?generatableType.getPackage().subPackage("enum"):generatableType.getPackage();
            javaType = ruleFactory.getEnumRule().apply(nodeName, schemaNode, parent, enumPackage, schema);
        } else {
            javaType = ruleFactory.getTypeRule().apply(nodeName, schemaNode, parent, generatableType.getPackage(), schema);
        }
        schema.setJavaTypeIfEmpty(javaType);

        return javaType;
    }

    private String nameFromRef(String ref) {

        if ("#".equals(ref)) {
            return null;
        }

        String nameFromRef;
        if (!contains(ref, "#")) {
            nameFromRef = getNodeName(ref, ruleFactory.getGenerationConfig());
        } else {
            String[] nameParts = split(ref, "/\\#");
            nameFromRef = nameParts[nameParts.length - 1];
        }

        try {
            return URLDecoder.decode(nameFromRef, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new GenerationException("Failed to decode ref: " + ref, e);
        }
    }
    private String getNodeName(String filePath, GenerationConfig config) {
        try {
            String fileName = FilenameUtils.getName(URLDecoder.decode(filePath, StandardCharsets.UTF_8.toString()));
            String[] extensions = config.getFileExtensions() == null ? new String[]{} : config.getFileExtensions();

            boolean extensionRemoved = false;
            for (String s : extensions) {
                String extension = s;
                if (extension.length() == 0) {
                    continue;
                }
                if (!extension.startsWith(".")) {
                    extension = "." + extension;
                }
                if (fileName.endsWith(extension)) {
                    fileName = removeEnd(fileName, extension);
                    extensionRemoved = true;
                    break;
                }
            }
            if (!extensionRemoved) {
                fileName = FilenameUtils.getBaseName(fileName);
            }
            return fileName;
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException(String.format("无法从URL生成节点名称: %s", filePath), e);
        }
    }


}
